using System;
using System.Data;
using System.Configuration;
using System.Reflection;
using System.Collections;
using System.Collections.Generic;

namespace RunAliasCreator
{
	/// <summary>
	/// Provides convenient methods for miscellaneous features.
	/// </summary>
	public static class Functions
	{
		/// <summary>
		/// Merges to IList objects together.
		/// </summary>
		/// <param name="list1">The list which will have new items added to it.</param>
		/// <param name="list2">The list containing items to be added.</param>
		public static void MergeLists(ref IList list1, IList list2)
		{
			foreach (object item in list2)
			{
				if (!list1.Contains(item))
					list1.Add(item);
			}
		}

		/// <summary>
		/// Merges two objects of the same type together.
		/// </summary>
		/// <typeparam name="T">The type of the objects to be merged.</typeparam>
		/// <param name="object1">The object which will be altered.</param>
		/// <param name="object2">The object which will serve as the primary data source.</param>
		public static void MergeObjects<T>(ref T object1, T object2)
		{
			Type t = typeof(T);
			PropertyInfo[] properties = t.GetProperties();
			Type pType;
			object pValue;
			bool isBlank;
			foreach (PropertyInfo p in properties)
			{
				pType = p.PropertyType;
				pValue = p.GetValue(object1, null);
				isBlank = false;
				if (pValue == null)
					isBlank = true;
				else if (pType.IsAssignableFrom(typeof(string)) && pValue.Equals(string.Empty))
					isBlank = true;
				else if (pType.IsAssignableFrom(typeof(Guid)) && pValue.Equals(default(Guid)))
					isBlank = true;
				else if (pType.IsAssignableFrom(typeof(DateTime)) && pValue.Equals(default(DateTime)))
					isBlank = true;
				else if (pType.IsAssignableFrom(typeof(bool)) && pValue.Equals(default(bool)))
					isBlank = true;
				else if (pType.IsAssignableFrom(typeof(byte)) && pValue.Equals(default(byte)))
					isBlank = true;
				else if (pType.IsAssignableFrom(typeof(sbyte)) && pValue.Equals(default(sbyte)))
					isBlank = true;
				else if (pType.IsAssignableFrom(typeof(char)) && pValue.Equals(default(char)))
					isBlank = true;
				if (isBlank)
				{
					pValue = p.GetValue(object2, null);
					p.SetValue(object1, pValue, null);
				}
			}
		}

		/// <summary>
		/// Applies the specified capitalization style to the specified text
		/// </summary>
		/// <param name="text">The text to be converted.</param>
		/// <param name="capsStyle">One of the Gpj.Onsite.Common.CapitalizationStyle
		/// values that specifies how capitalization should be done during the
		/// conversion.</param>
		/// <returns>A System.String containing the converted text.</returns>
		public static string Capitalize(string text, CapitalizationStyle capsStyle)
		{
			if (text == null) return null;
			string buffer = string.Empty;
			char current, last = '\0';
			for (int i = 0; i < text.Length; i++)
			{
				current = text[i];
				// If the letter is in upper case, or we encounter an underscore, treat
				// this as a new word
				if (i > 0 && last == ' ')
				{
					buffer += " ";
					switch (capsStyle)
					{
						case CapitalizationStyle.All:
						case CapitalizationStyle.FirstLetterOfWord:
							buffer += char.ToUpper(current);
							break;
						case CapitalizationStyle.None:
						case CapitalizationStyle.FirstLetter:
							buffer += char.ToLower(current);
							break;
						case CapitalizationStyle.Default:
						default:
							buffer += current;
							break;
					}
				}
				// If this is the first character, act accordingly
				else if (i == 0)
				{
					switch (capsStyle)
					{
						case CapitalizationStyle.All:
						case CapitalizationStyle.FirstLetter:
						case CapitalizationStyle.FirstLetterOfWord:
							buffer += char.ToUpper(current);
							break;
						case CapitalizationStyle.None:
							buffer += char.ToLower(current);
							break;
						case CapitalizationStyle.Default:
						default:
							buffer += current;
							break;
					}
				}
				// We want to ignore spaces when they come up, they're handled
				// with the character that follows them
				else if (current == ' ')
				{
					// Do nothing
				}
				// For everything else, just treat it normally
				else
				{
					switch (capsStyle)
					{
						case CapitalizationStyle.All:
							buffer += char.ToUpper(current);
							break;
						case CapitalizationStyle.None:
							buffer += char.ToLower(current);
							break;
						case CapitalizationStyle.FirstLetter:
						case CapitalizationStyle.FirstLetterOfWord:
						case CapitalizationStyle.Default:
						default:
							buffer += current;
							break;
					}
				}
				last = current;
			}
			return buffer;
		}

		/// <summary>
		/// Converts a code name from one of the various naming conventions into a
		/// user-friendly name.
		/// </summary>
		/// <param name="codeName">The code name to be converted.</param>
		/// <returns>A System.String containing the user-friendly name.</returns>
		public static string CodeNameToPrettyName(string codeName)
		{ return CodeNameToPrettyName(codeName, CapitalizationStyle.Default); }

		/// <summary>
		/// Converts a code name from one of the various naming conventions into a
		/// user-friendly name.
		/// </summary>
		/// <param name="codeName">The code name to be converted.</param>
		/// <param name="capsStyle">One of the Gpj.Onsite.Common.CapitalizationStyle
		/// values that specifies how capitalization should be done during the
		/// conversion.</param>
		/// <returns>A System.String containing the user-friendly name.</returns>
		public static string CodeNameToPrettyName(string codeName,
			CapitalizationStyle capsStyle)
		{
			if (codeName == null) return null;
			string buffer = string.Empty;
			char current, last = '\0';
			string useName = codeName;
			bool starting = true;
			// If the entire thing is in caps, let's just ignore case altogether
			if (useName == useName.ToUpper())
				useName = useName.ToLower();
			for (int i = 0; i < codeName.Length; i++)
			{
				current = useName[i];
				// If the letter is in upper case, or we encounter a symbol, treat
				// this as a new word
				if (!starting && (char.IsUpper(current) || !char.IsLetterOrDigit(last)))
				{
					buffer += " ";
					switch (capsStyle)
					{
						case CapitalizationStyle.All:
						case CapitalizationStyle.FirstLetterOfWord:
							buffer += char.ToUpper(current);
							break;
						case CapitalizationStyle.None:
						case CapitalizationStyle.FirstLetter:
							buffer += char.ToLower(current);
							break;
						case CapitalizationStyle.Default:
						default:
							buffer += current;
							break;
					}
				}
				// If this is the first character, act accordingly
				else if (starting && char.IsLetterOrDigit(current))
				{
					starting = false;
					switch (capsStyle)
					{
						case CapitalizationStyle.All:
						case CapitalizationStyle.FirstLetter:
						case CapitalizationStyle.FirstLetterOfWord:
							buffer += char.ToUpper(current);
							break;
						case CapitalizationStyle.None:
							buffer += char.ToLower(current);
							break;
						case CapitalizationStyle.Default:
						default:
							buffer += current;
							break;
					}
				}
				// We want to ignore symbols when they come up, they're handled
				// with the character that follows them
				else if (!char.IsLetterOrDigit(current))
				{
					// Do nothing
				}
				// For everything else, just treat it normally
				else
				{
					switch (capsStyle)
					{
						case CapitalizationStyle.All:
							buffer += char.ToUpper(current);
							break;
						case CapitalizationStyle.None:
							buffer += char.ToLower(current);
							break;
						case CapitalizationStyle.FirstLetter:
						case CapitalizationStyle.FirstLetterOfWord:
						case CapitalizationStyle.Default:
						default:
							buffer += current;
							break;
					}
				}
				last = current;
			}
			return buffer;
		}

		/// <summary>
		/// Gets the name of the enumeration field that corresponds to the specified value.
		/// </summary>
		/// <param name="value">The value to search for within its defined enumeration.</param>
		/// <returns>A System.String containing the name of the enumeration field.</returns>
		public static string GetEnumerationName(object value)
		{
			Type t = value.GetType();
			if (!t.IsEnum) return null;
			FieldInfo[] fields = t.GetFields();
			foreach (FieldInfo f in fields)
			{
				if (f.GetValue(value).Equals(value)) return f.Name;
			}
			return null;
		}

		/// <summary>
		/// Gets the user-friendly name of the enumeration field that corresponds to the specified
		/// value.
		/// </summary>
		/// <param name="value">The value to search for within its defined enumeration.</param>
		/// <returns>A System.String containing the user-friendly name of the enumeration
		/// field.</returns>
		public static string GetEnumerationPrettyName(object value)
		{ return GetEnumerationPrettyName(value, CapitalizationStyle.Default); }
		/// <summary>
		/// Gets the user-friendly name of the enumeration field that corresponds to the specified
		/// value.
		/// </summary>
		/// <param name="value">The value to search for within its defined enumeration.</param>
		/// <param name="capsStyle">One of the Gpj.Onsite.Common.CapitalizationStyle values that
		/// specifies how capitalization should be done during the conversion.</param>
		/// <returns>A System.String containing the user-friendly name of the enumeration
		/// field.</returns>
		public static string GetEnumerationPrettyName(object value, CapitalizationStyle capsStyle)
		{
			return CodeNameToPrettyName(GetEnumerationName(value), capsStyle);
		}

		/// <summary>
		/// Unescapes the specified escape sequence.
		/// </summary>
		/// <param name="escapeSequence">The escaped string to process.</param>
		/// <returns>A System.String containing the unescaped string.</returns>
		public static string GetEscape(string escapeSequence)
		{
			if (escapeSequence == null) return null;
			if (escapeSequence.Length < 2) return escapeSequence;
			string prefix = escapeSequence.Substring(0, 2);
			string escaped = escapeSequence.Substring(1);
			string postfix = string.Empty;
			if (escapeSequence.Length > 2) postfix = escapeSequence.Substring(2);
			uint postfixValue = 0;
			switch (prefix.ToLower())
			{
				case @"\a":
					return "\a";
				case @"\b":
					return "\b";
				case @"\f":
					return "\f";
				case @"\n":
					return "\n";
				case @"\r":
					return "\r";
				case @"\t":
					return "\t";
				case @"\v":
					return "\v";
				case @"\\":
					return "\\";
				case @"\0":
					if (postfix.Length < 2) return null;
					if (postfix.Length > 2) return escaped;
					postfixValue = Convert.ToUInt32(postfix, 8);
					if (postfixValue > char.MaxValue) return char.MaxValue.ToString();
					if (postfixValue > char.MaxValue) return char.MinValue.ToString();
					return ((char)postfixValue).ToString();
				case @"\x":
					if (postfix.Length < 2) return null;
					if (postfix.Length > 2) return escaped;
					postfixValue = Convert.ToUInt32(postfix, 16);
					if (postfixValue > char.MaxValue) return char.MaxValue.ToString();
					if (postfixValue > char.MaxValue) return char.MinValue.ToString();
					return ((char)postfixValue).ToString();
				case @"\u":
					if (postfix.Length < 4) return null;
					if (postfix.Length > 4) return escaped;
					postfixValue = Convert.ToUInt32(postfix, 16);
					if (postfixValue > char.MaxValue) return char.MaxValue.ToString();
					if (postfixValue > char.MaxValue) return char.MinValue.ToString();
					return ((char)postfixValue).ToString();
				default:
					return escaped;
			}
		}

	}



	/// <summary>
	/// Specifies the capitalization style to use when formatting a group of words.
	/// words.
	/// </summary>
	public enum CapitalizationStyle
	{
		/// <summary>
		/// Do not change the capitalization style.
		/// </summary>
		Default,
		/// <summary>
		/// Do not capitalize any letters.
		/// </summary>
		None,
		/// <summary>
		/// Capitalize all letters in the text.
		/// </summary>
		All,
		/// <summary>
		/// Capitalize the first letter in the text.
		/// </summary>
		FirstLetter,
		/// <summary>
		/// Capitalize the first letter of each word.
		/// </summary>
		FirstLetterOfWord,
	}

}
